/*
 Xern Entity Relocation Network
 Copyright (C) 2010-2011 Ronie Salgado <roniesalg@gmail.com>

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#ifndef XERN_REGIONFILE_HPP
#define XERN_REGIONFile_HPP

#include "DataTypes.hpp"
#include "DataStream.hpp"

namespace Xern
{
	// Region file header constants.
	const u32 RegionSignature = XERN_FOURCC('X', 'R', 'G', 'N');
	const size_t RegionHeaderSize = 58;

	// Region file header layout.
	struct RegionFileHeader
	{
		u32 signature;
		u16 headerSize;
		u32 stringTableOffset;
		u32 stringTableSize;
		u16 numobjects;
		u32 objectTableOffset;
		u32 objectTableSize;
		u32 meshTableOffset;
		u32 meshTableSize;
		u32 collisionTableOffset;
		u32 collisionTableSize;
		u32 fsOffset;
		u32 fsSize;
		u32 extensionOffset;
		u32 extensionSize;
		u16 rootObject;
	};

	inline DataStream &operator<<(DataStream &out, const RegionFileHeader &h)
	{
		out << h.signature << h.headerSize << h.stringTableOffset;
		out << h.stringTableSize << h.numobjects;
		out << h.objectTableOffset << h.objectTableSize;
		out << h.meshTableOffset << h.meshTableSize;
		out << h.collisionTableOffset << h.collisionTableSize;
		out << h.fsOffset << h.fsSize << h.extensionOffset;
		out << h.extensionSize << h.rootObject;
		return out;
	}

	inline DataStream &operator>>(DataStream &in, RegionFileHeader &h)
	{
		in >> h.signature >> h.headerSize >> h.stringTableOffset;
		in >> h.stringTableSize >> h.numobjects;
		in >> h.objectTableOffset >> h.objectTableSize;
		in >> h.meshTableOffset >> h.meshTableSize;
		in >> h.collisionTableOffset >> h.collisionTableSize;
		in >> h.fsOffset >> h.fsSize >> h.extensionOffset;
		in >> h.extensionSize >> h.rootObject;
		return in;
	}

	// Region file body type.
	enum RegionFileBodyType
	{
		RFBT_STATIC = 0,
		RFBT_RIGID
	};

	// Region file object header(and layout).
	struct RegionFileObject
	{
		u16 className;
		u16 name;
		u8 uuid[16];
		f32 position[3];
		f32 matrix[3*3];
		u8 bodyType;
		f32 mass;
		u16 nummeshes;
		u16 numchildren;
		u16 numproperties;
		u16 numcollisionShapes;
		// u16 meshes[nummeshes];
		// u16 children[numchildren];
		// u16 properties[numproperties*2];
		// u16 collisionShapes[numcollisionShape];
	};

	inline DataStream &operator<<(DataStream &out, const RegionFileObject &o)
	{
		out << o.className << o.name;
		for(int i = 0; i < 16; i++)
			out << o.uuid[i];
		for(int i = 0; i < 3; i++)
			out << o.position[i];
		for(int i = 0; i < 3*3; i++)
			out << o.matrix[i];
		out << o.bodyType << o.mass;
		out << o.nummeshes << o.numchildren;
		out << o.numproperties << o.numcollisionShapes;
		return out;
	}

	inline DataStream &operator>>(DataStream &in, RegionFileObject &o)
	{
		in >> o.className >> o.name;
		for(int i = 0; i < 16; i++)
			in >> o.uuid[i];
		for(int i = 0; i < 3; i++)
			in >> o.position[i];
		for(int i = 0; i < 3*3; i++)
			in >> o.matrix[i];
		in >> o.bodyType >> o.mass;
		in >> o.nummeshes >> o.numchildren;
		in >> o.numproperties >> o.numcollisionShapes;
		return in;
	}

	// Region file mesh header and layout.
	struct RegionFileMesh
	{
		u16 name;
		u8 uuid[16];
		f32 boundingBoxMin[3];
		f32 boundingBoxMax[3];
		u8 vertexSize;
		u32 vertexFlags;
		u16 numsubmeshes;
		u16 numvertices;
		// Submeshes
		// Vertices
	};

	inline DataStream &operator<<(DataStream &out, const RegionFileMesh &m)
	{
		out << m.name;
		for(int i = 0; i < 16; i++)
			out << m.uuid[i];
		for(int i = 0; i < 3; i++)
			out << m.boundingBoxMin[i];
		for(int i = 0; i < 3; i++)
			out << m.boundingBoxMax[i];
		out << m.vertexSize << m.vertexFlags;
		out << m.numsubmeshes << m.numvertices;
		return out;
	}

	inline DataStream &operator>>(DataStream &in, RegionFileMesh &m)
	{
		in >> m.name;
		for(int i = 0; i < 16; i++)
			in >> m.uuid[i];
		for(int i = 0; i < 3; i++)
			in >> m.boundingBoxMin[i];
		for(int i = 0; i < 3; i++)
			in >> m.boundingBoxMax[i];
		in >> m.vertexSize >> m.vertexFlags;
		in >> m.numsubmeshes >> m.numvertices;
		return in;
	}

	// Region file sub mesh header and layout.
	struct RegionFileSubMesh
	{
		u16 texture;
		u8 indexSize;
		u16 numtriangles;
		// Triangles
	};

	inline DataStream &operator<<(DataStream &out, const RegionFileSubMesh &s)
	{
		out << s.texture << s.indexSize << s.numtriangles;
		return out;
	}

	inline DataStream &operator>>(DataStream &in, RegionFileSubMesh &s)
	{
		in >> s.texture >> s.indexSize >> s.numtriangles;
		return in;
	}

	// Region file collision shape type.
	enum RegionFileShapeType
	{
		RFST_SPHERE = 0,
		RFST_BOX,
		RFST_CONVEX_HULL,
		RFST_COMPOUND,
	};

	// Region file collision shape header and layout.
	struct RegionFileCollisionShape
	{
		u16 type;
		u8 uuid[16];

		union
		{
			struct
			{
				f32 radius; // Sphere radius.
			}sphere;

			struct
			{
				f32 min[3];
				f32 max[3];
			}box;

			struct
			{
				u16 numplanes;
				// f32[4] planes[numplanes];
			}convexHull;

			struct
			{
				u16 numsubshapes;
				// RegionFileSubCollisionShape [numsubshapes];
			}compound;
		};
	};

	inline DataStream &operator<<(DataStream &out, const RegionFileCollisionShape &s)
	{
		// Write the shape type.
		out << s.type;
		for(int i = 0; i < 16; i++)
			out << s.uuid[i];

		// Write the shape data.
		switch(s.type)
		{
		case RFST_SPHERE:
			out << s.sphere.radius;
			break;
		case RFST_BOX:
			out << s.box.min[0] << s.box.min[1] << s.box.min[2];
			out << s.box.max[0] << s.box.max[1] << s.box.max[2];
			break;
		case RFST_CONVEX_HULL:
			out << s.convexHull.numplanes;
			break;
		case RFST_COMPOUND:
			out << s.compound.numsubshapes;
			break;
		default:
			break;
		}

		return out;
	}

	inline DataStream &operator>>(DataStream &in, RegionFileCollisionShape &s)
	{
		// Read the shape type.
		in >> s.type;
		for(int i = 0; i < 16; i++)
			in >> s.uuid[i];

		// Read the shape data.
		switch(s.type)
		{
		case RFST_SPHERE:
			in >> s.sphere.radius;
			break;
		case RFST_BOX:
			in >> s.box.min[0] >> s.box.min[1] >> s.box.min[2];
			in >> s.box.max[0] >> s.box.max[1] >> s.box.max[2];
			break;
		case RFST_CONVEX_HULL:
			in >> s.convexHull.numplanes;
			break;
		case RFST_COMPOUND:
			in >> s.compound.numsubshapes;
			break;
		default:
			break;
		}

		return in;
	}

	// Sub collision shape.
	struct RegionFileSubCollisionShape
	{
		static const size_t Size = 30;
		f32 position[3];
		f32 rotation[4]; // wxyz quaternion.
		u16 shape;
	};

	inline DataStream &operator<<(DataStream &out, const RegionFileSubCollisionShape &s)
	{
		for(int i = 0; i < 3; i++)
			out << s.position[i];
		for(int i = 0; i < 4; i++)
			out << s.rotation[i];
		out << s.shape;
	}

	inline DataStream &operator>>(DataStream &in, RegionFileSubCollisionShape &s)
	{
		for(int i = 0; i < 3; i++)
			in >> s.position[i];
		for(int i = 0; i < 4; i++)
			in >> s.rotation[i];
		in >> s.shape;
	}
};

#endif //XERN_REGION_HPP
